{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "49f4061f",
   "metadata": {},
   "outputs": [],
   "source": [
    "datasets = ['CUB', 'Derm7pt', 'RIVAL10']\n",
    "use_dataset = datasets[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cf32b6b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "import seaborn as sns\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "notebook_dir = os.getcwd()\n",
    "project_root_path = os.path.dirname(notebook_dir)\n",
    "sys.path.insert(0, project_root_path)\n",
    "\n",
    "from src.config import PROJECT_ROOT, CUB_CONFIG, DERM7PT_CONFIG, RIVAL10_CONFIG  # noqa: E402\n",
    "import numpy as np  # noqa: E402"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ce61bea4",
   "metadata": {},
   "outputs": [],
   "source": [
    "DATASET_PATH =  os.path.join(PROJECT_ROOT, 'output', use_dataset)\n",
    "if use_dataset == 'CUB':\n",
    "    config_dict = CUB_CONFIG\n",
    "elif use_dataset == 'Derm7pt':\n",
    "    config_dict = DERM7PT_CONFIG\n",
    "else:\n",
    "    config_dict = RIVAL10_CONFIG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "d915a462",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_concepts = config_dict['N_TRIMMED_CONCEPTS']\n",
    "num_classes = config_dict['N_CLASSES']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c61261b3",
   "metadata": {},
   "source": [
    "# Get prototype and concept names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "4e4458c2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['bird', 'car', 'cat', 'deer', 'dog', 'equine', 'frog', 'plane', 'ship', 'truck']\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "def get_class_names(label_mappings_path):\n",
    "    \"\"\"\n",
    "    Reads the label_mappings.json file to determine the correct order of class names\n",
    "    as implicitly determined by scikit-learn's OneHotEncoder.\n",
    "    \"\"\"\n",
    "    with open(label_mappings_path, 'r') as f:\n",
    "        label_mappings = json.load(f)\n",
    "\n",
    "    # The super-class is the first element in the value list (e.g., \"truck\" in [\"truck\", 0])\n",
    "    super_classes = {mapping[0] for mapping in label_mappings.values()}\n",
    "\n",
    "    # OneHotEncoder sorts the unique classes alphabetically to create the encoding\n",
    "    sorted_class_names = sorted(list(super_classes))\n",
    "\n",
    "    return sorted_class_names\n",
    "\n",
    "# --- HOW TO USE IT ---\n",
    "# Replace with the actual path to your file\n",
    "label_mappings_file = os.path.join(PROJECT_ROOT, 'data', 'RIVAL10', 'meta', 'label_mappings.json')\n",
    "class_names = get_class_names(label_mappings_file)\n",
    "\n",
    "# This will print the list that maps an index to a class name\n",
    "# e.g., class_names[0] is 'bird', class_names[1] is 'car', etc.\n",
    "print(class_names)\n",
    "# Expected output:\n",
    "# ['bird', 'car', 'cat', 'deer', 'dog', 'equine', 'frog', 'plane', 'ship', 'truck']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "ea8a4cae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Scanning directories to find all unique concepts...\n",
      "\n",
      "Found 19 unique concepts.\n",
      "\n",
      "Reconstructed Concept Name List (Index -> Name):\n",
      "0: beak\n",
      "1: colored-eyes\n",
      "2: ears\n",
      "3: entire-object\n",
      "4: floppy-ears\n",
      "5: hairy\n",
      "6: horns\n",
      "7: long\n",
      "8: long-snout\n",
      "9: mane\n",
      "10: metallic\n",
      "11: patterned\n",
      "12: rectangular\n",
      "13: tail\n",
      "14: tall\n",
      "15: text\n",
      "16: wet\n",
      "17: wheels\n",
      "18: wings\n"
     ]
    }
   ],
   "source": [
    "\n",
    "import pickle\n",
    "\n",
    "\n",
    "def reconstruct_concept_list(project_root):\n",
    "    \"\"\"\n",
    "    Scans all _attr_dict.pkl files in the RIVAL10 dataset to reconstruct\n",
    "    the alphabetically sorted master list of concept names.\n",
    "    \"\"\"\n",
    "    train_path = os.path.join(project_root, 'data', 'RIVAL10', 'train', 'ordinary')\n",
    "    test_path = os.path.join(project_root, 'data', 'RIVAL10', 'test', 'ordinary')\n",
    "\n",
    "    all_concept_names = set()\n",
    "\n",
    "    print(\"Scanning directories to find all unique concepts...\")\n",
    "\n",
    "    # Scan both training and testing directories\n",
    "    for dir_path in [train_path, test_path]:\n",
    "        if not os.path.isdir(dir_path):\n",
    "            print(f\"Warning: Directory not found, skipping: {dir_path}\")\n",
    "            continue\n",
    "\n",
    "        for filename in os.listdir(dir_path):\n",
    "            if filename.endswith('_attr_dict.pkl'):\n",
    "                file_path = os.path.join(dir_path, filename)\n",
    "                with open(file_path, 'rb') as f:\n",
    "                    try:\n",
    "                        data = pickle.load(f)\n",
    "                        # Add all concept names from this file to our set\n",
    "                        all_concept_names.update(data.keys())\n",
    "                    except Exception as e:\n",
    "                        print(f\"Error reading {filename}: {e}\")\n",
    "\n",
    "    # Sort the unique names alphabetically to get the final ordered list\n",
    "    sorted_concepts = sorted(list(all_concept_names))\n",
    "\n",
    "    print(f\"\\nFound {len(sorted_concepts)} unique concepts.\")\n",
    "\n",
    "    # This is the master list we need\n",
    "    return sorted_concepts\n",
    "\n",
    "# Run the reconstruction\n",
    "concept_names = reconstruct_concept_list(PROJECT_ROOT)\n",
    "\n",
    "# Print the final list to verify\n",
    "print(\"\\nReconstructed Concept Name List (Index -> Name):\")\n",
    "for i, name in enumerate(concept_names):\n",
    "    print(f\"{i}: {name}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "811012eb",
   "metadata": {},
   "source": [
    "# Load and Transform Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "90493c03",
   "metadata": {},
   "outputs": [],
   "source": [
    "prototypes = np.load(os.path.join(DATASET_PATH, 'learned_prototypes.npy'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "11e5d989",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.,\n",
       "        0., 1.],\n",
       "       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0.,\n",
       "        0., 0.],\n",
       "       [0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0., 1., 0., 0., 0., 0.,\n",
       "        0., 1.],\n",
       "       [1., 0., 0., 0., 1., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0.,\n",
       "        0., 1.],\n",
       "       [1., 0., 0., 0., 0., 1., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0.,\n",
       "        0., 0.],\n",
       "       [1., 0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 1., 0., 0., 0., 0.,\n",
       "        0., 1.],\n",
       "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,\n",
       "        0., 1.],\n",
       "       [0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1.,\n",
       "        1., 1.],\n",
       "       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.,\n",
       "        1., 0.],\n",
       "       [0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 1.,\n",
       "        1., 0.]], dtype=float32)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prototypes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f568ba5c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZAAAAGNCAYAAAA/9yeJAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAP49JREFUeJzt3Qd4FFX7NvAnBAg99N57L4LSlCJNQPorvRdF6YgUC4QuRRRB6S9dBJHuCygovRcpCghKr9J7gDDfdZ/rP/tNNpvs7mQ3O7u5f9c1GmZ3Z86085w2M0GapmlCRETkpgTu/oCIiAgYQIiIyBQGECIiMoUBhIiITGEAISIiUxhAiIjIFAYQIiIyhQGEiIhMYQAhIiJTGEA8ICwsTIKCgsSfzZs3T23DuXPnfJ0UIp+fk1u2bFHrxv8pDgLIsWPH5D//+Y/kypVLkiRJItmyZZNatWrJlClTxNceP36sMnmrnAxIR9OmTSVz5sySOHFiyZgxozRo0EBWrFjh8XVFRETI3LlzpVq1apI2bVoJCQmR3LlzS6dOneTAgQMSV/73v/+pYxCXOnbsqDIBfUqVKpWUKlVKvvjiCwkPD/fYeq5cuaK27ffff/er/RNbJ06cUPsV1/vdu3dNL2fMmDGyatUq8YVvv/1WBSorqVatmtqvBQoUcPj5L7/8Yjunly9fLj6lecDOnTu1xIkTa/nz59dGjhypzZo1Sxs6dKhWu3ZtLV++fJqv/fvvv3jelzZs2DCvLB/LdXVXYr/guwUKFFB/z5kzRxs/frxWrVo1NX/x4sUeS9fjx4+1t956Sy23SpUq2oQJE9T6PvvsM61QoUJaUFCQdvHiRfXduXPnqu+dPXtW84YePXq4vI88pUOHDlpISIi2cOFCNU2ZMsW2n1u0aOGx9ezfv18tE/vQn/ZPbH388cda5syZ1T7GNW9W8uTJ1bGy9+LFC+3Jkyfay5cvNW8pVqyYVrVq1SjzIyIi1Lrx/7hWtWpVLUmSJOp82Lt3b5TPsa/0z3/44QfNlxJ6IgiNHj1aQkNDZf/+/ZI6depIn924cUP8zaNHjyR58uQeXy5KCyNGjFA1te+++04SJUpk++yjjz6SjRs3yvPnzz22Pixzw4YN8uWXX0rfvn0jfTZs2DA135/hOaBPnz6VpEmTRvudhAkTStu2bW3//uCDD6R8+fKydOlSmTRpkmTNmtXUcuM77COcw61bt5azZ8/K4sWLpWvXrh5dR3BwsJp8IUGCBKpm5Sv58uWTFy9eyJIlS+S1116zzcd5uXLlSqlfv778+OOP4nOeiEIozaJk5wqsEqWtRYsWaQULFlSll1deeUXbunVrlO9eunRJ69Spk5YxY0ZVwylatKgqQdtDSQG1AJTqsTyUipo0aaKdOXNGlaixTvtJr40gmqMEhO/WrVtXS5EihdaoUSP12bZt27T//Oc/Wo4cOdT6s2fPrvXt21eV7M3UQAoXLqylTZtWu3//vkv76vr161rnzp3V9mO7SpYsqc2bN8+l36JmkTBhQq1WrVoufd9RDSS6WluuXLkilRifPXumhYWFqRoo0oltrFy5svbzzz+rz/FdR8dAh1Lel19+qY4vfo/tfffdd7Xbt29HWW/9+vW1DRs2aGXLllXfxe+iox9bewMGDFDrR83Z2XL//vtvdQ6kSZNGS5o0qVa+fHlt3bp1tmX99ttvDrfNWBtZtmyZOsdRakyXLp3Wpk0bdW4b0+loGSh5I20NGzZ0eM6nSpVK7SdjOr7//nttyJAhWqZMmbRkyZJpDRo00C5cuBDl93v27NHq1KmjloHtQg11x44dmqu2b9+u1rdv3z5t6dKlWoIECWy1WSMc26+++korXry42q/p06dX60WtDRxtt35u2Z+TOEZ58uRxmJ4KFSqoY6f773//q1WvXl3LkCGDunaLFCmiffvtt5F+g31rv269NqLvT/zfyNmxNJ53mI+8BH9juz/88ENVq3IGaUDNCNdUlixZItWCsH5c19jn9jWQc+fOae+//77KV5E+XIc4d+1bFfT9ijwX5w++lzJlSq1du3ZRrrk4qYGg32P37t1y/PhxKV68uNPvb926VZUAe/furdrk0Q751ltvyb59+2y/v379ulSoUEG18/Xs2VMyZMgg69evly5dusj9+/dtJWq08b/99tuyefNmadmypfTp00cePHig2gmRnpo1a8q0adPk/ffflyZNmqi+ByhZsqQtPYj0derUkddff10mTpwoyZIlU/N/+OEH1X+C36ZLl06lD306ly5dUp+54/Tp03Ly5Enp3LmzpEyZ0un3nzx5otpCz5w5o7Y/T548ap1o10d7M7YzJthX2K527dqJt6HtfuzYsaoEitISjg/6Vw4dOqT6wd577z3VT4BjsnDhwii/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1kCiYg2E4qwGYv/IEiIiit/cvg+EiIgIGECIiMgUBhAiIjKFAYSIiExhACEiorgLIHjd7OzZs2XIkCFy+/ZtNe/QoUNy+fJlc6kgIqLAf5TJ0aNHpWbNmuq9H+fOnZNu3bpJ2rRpZcWKFXLhwgVZsGCBd1JKRET+XQPBK2g7duwop0+fliRJktjm16tXT7Zt2+bp9BERUaAEkP3798t7770XZX62bNnUI96JiCh+cDuAhISEyP3796PM/+uvvyRDhgyeShcREQVaAMHDEkeMGCHPnz+3PU8HfR+DBg2SZs2aubUsNHk1aNBAsmbNqpazatUqd5NDRET+EkC++OILefjwoWTMmFGePHkiVatWVe9HT5kypYwePdqtZT169EhKlSol33zzjbvJICIif30fyI4dO9SILASTV155RY3MilVCgoJk5cqV0rhx41gth8gRPo03Kj6Nl+J8GK/u9ddfVxMREcVPpm4k3Lx5s7z99tuSL18+NeHvTZs2eT51REQUOAHk22+/lbfeekv1efTp00dNqVKlUveBeLsvIzw8XI0AM06YR0REPqC5KVu2bNqUKVOizJ86daqWNWtWzSwkZeXKlTF+Z9iwYep7xgnziOIb++vAzBRI2+Kpidzjdid6ihQp5Pfff1cjr4xwZ3qZMmVUp7q3OtFR27CvceC+FExE8UkgDQqwSme+lfZJQN8Hgoze3urVq1VfiDsQbBCMMMHZs2fV37ivxBEECjSXGScGDyIi33C7BjJq1CiZOHGiVK5cWSpWrKjm7dmzR3bu3CkffvihytR1vXv3jnFZW7ZskerVq0eZ36FDB5k3b547ySKKV1gD8Q6r7JOADSB58uRxbcFBQfLPP/+YTRcRxYABxDussk8C/kZCIvIdBhDvsMo+Cdg+kN9++807KSEiosAOILgHBDcPoi/k4sWL3kkVEREFXgDBa2t79uwpy5cvl7x580qdOnVk2bJl8uzZM++kkIiIAq8PBO9Bnzt3rixZskT9u3Xr1tKlSxf1hF0i8h72gXiHVfZJvOlEv3LlisycOVM+//xzSZgwoTx9+lQN750+fboUK1bMcyklIhsGEO+wyj4J6Icp4mVSaMLC869y5colGzdulKlTp8r169flzJkzat4777zj+dQSEZH/1kB69eqlmqzws3bt2knXrl2lePHikb6Dd6PjLYMvX770dHqJ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",
      "text/plain": [
       "<Figure size 400x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.cluster import SpectralCoclustering\n",
    "\n",
    "\n",
    "row_sums = prototypes.sum(axis=1)\n",
    "col_sums = prototypes.sum(axis=0)\n",
    "\n",
    "prototypes[row_sums == 0, :] = 1e-10\n",
    "prototypes[:, col_sums == 0] = 1e-10\n",
    "\n",
    "if use_dataset=='CUB':\n",
    "    k = 3\n",
    "else:\n",
    "    k=2\n",
    "\n",
    "model = SpectralCoclustering(n_clusters=k, random_state=0)\n",
    "model.fit(prototypes)\n",
    "\n",
    "row_perm = np.argsort(model.row_labels_)\n",
    "col_perm = np.argsort(model.column_labels_)\n",
    "\n",
    "# Apply permutation to matrix\n",
    "spectral_prototypes = prototypes[row_perm][:, col_perm]\n",
    "\n",
    "# Plot with correct original indices as ticks\n",
    "plt.figure(figsize=(4,4))\n",
    "sns.heatmap(spectral_prototypes, cmap='Greys', cbar=False)\n",
    "\n",
    "plt.xlabel(\"Concept Index\")\n",
    "plt.ylabel(\"Prototype Index\")\n",
    "\n",
    "# Set ticks to original indices (after reordering)\n",
    "plt.yticks(ticks=np.arange(len(row_perm)), labels=row_perm, rotation=0)\n",
    "plt.xticks(ticks=np.arange(len(col_perm)), labels=col_perm, rotation=90)\n",
    "\n",
    "plt.title(\"Spectral Co-Cluster Prototype Activation Map\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f774bc36",
   "metadata": {},
   "outputs": [],
   "source": [
    "cluster_assignments = model.row_labels_\n",
    "\n",
    "clusters_labels = {0: np.array(np.where(cluster_assignments==0)),\n",
    "                    1: np.array(np.where(cluster_assignments==1)),\n",
    "                    2: np.array(np.where(cluster_assignments==2))\n",
    "                    }\n",
    "\n",
    "clusters_prototypes = {0: prototypes[np.where(cluster_assignments==0)],\n",
    "                        1: prototypes[np.where(cluster_assignments==1)],\n",
    "                        2: prototypes[np.where(cluster_assignments==2)]\n",
    "                        }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78a0d1f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from collections import deque\n",
    "\n",
    "class TreeNode:\n",
    "    def __init__(self, prototypes, prototype_labels=None, concepts=None, parent=None):\n",
    "        self.prototypes = prototypes\n",
    "        # Ensure labels are a NumPy array for consistent, fast operations.\n",
    "        self.prototype_labels = np.asarray(prototype_labels) if prototype_labels is not None else np.array([])\n",
    "        self.concepts = concepts if concepts is not None else []\n",
    "        self.children = []\n",
    "        self.parent = parent\n",
    "        self.is_leaf = False\n",
    "        self.node_id = None\n",
    "        # Internal attribute to cache all pure concepts for this node's group\n",
    "        self._total_pure_concepts = set()\n",
    "\n",
    "def build_concept_tree(prototypes, prototype_labels=None, max_leaf_size=1):\n",
    "    if prototype_labels is None:\n",
    "        prototype_labels = np.arange(len(prototypes))\n",
    "\n",
    "    # Ensure a NumPy array is used from the start for labels.\n",
    "    prototype_labels = np.asarray(prototype_labels)\n",
    "\n",
    "    root = TreeNode(prototypes, prototype_labels)\n",
    "    queue = deque([root])\n",
    "    node_counter = 0\n",
    "\n",
    "    while queue:\n",
    "        current = queue.popleft()\n",
    "        current.node_id = node_counter\n",
    "        node_counter += 1\n",
    "\n",
    "        total_prototypes = len(current.prototypes)\n",
    "        if total_prototypes == 0:\n",
    "            current.is_leaf = True\n",
    "            continue\n",
    "\n",
    "        concept_counts = np.sum(current.prototypes, axis=0)\n",
    "\n",
    "        # --- REQUIREMENT 1: CONCEPT INHERITANCE LOGIC ---\n",
    "        # 1. Calculate ALL concepts that are pure for this node's prototypes.\n",
    "        my_total_pure_concepts = set(np.where(concept_counts == total_prototypes)[0])\n",
    "        current._total_pure_concepts = my_total_pure_concepts  # Cache for its children\n",
    "\n",
    "        # 2. Get the pure concepts from the parent (efficiently, from its cache).\n",
    "        parent_pure_concepts = current.parent._total_pure_concepts if current.parent else set()\n",
    "\n",
    "        # 3. This node's public `concepts` list should only contain what's new.\n",
    "        new_concepts = my_total_pure_concepts - parent_pure_concepts\n",
    "        current.concepts.extend(sorted(list(new_concepts)))\n",
    "\n",
    "        # --- Leaf condition checks (no changes needed here) ---\n",
    "        if total_prototypes <= max_leaf_size:\n",
    "            current.is_leaf = True\n",
    "            continue\n",
    "\n",
    "        non_pure_concepts_mask = (concept_counts > 0) & (concept_counts < total_prototypes)\n",
    "        if not np.any(non_pure_concepts_mask):\n",
    "            current.is_leaf = True\n",
    "            continue\n",
    "\n",
    "        # --- Efficient Child Creation (no changes needed here) ---\n",
    "        unassigned_mask = np.ones(total_prototypes, dtype=bool)\n",
    "        remaining_concept_counts = concept_counts.copy()\n",
    "        num_remaining_prototypes = total_prototypes\n",
    "\n",
    "        while num_remaining_prototypes > 0:\n",
    "            valid_mask = (remaining_concept_counts > 0) & non_pure_concepts_mask\n",
    "            if not np.any(valid_mask):\n",
    "                break\n",
    "\n",
    "            valid_indices = np.where(valid_mask)[0]\n",
    "            frequencies = remaining_concept_counts[valid_indices] / num_remaining_prototypes\n",
    "            best_concept = valid_indices[np.argmax(frequencies)]\n",
    "\n",
    "            child_mask = (current.prototypes[:, best_concept] == 1) & unassigned_mask\n",
    "            num_child_prototypes = np.sum(child_mask)\n",
    "\n",
    "            if num_child_prototypes == 0:\n",
    "                remaining_concept_counts[best_concept] = 0\n",
    "                continue\n",
    "\n",
    "            child_prototypes = current.prototypes[child_mask]\n",
    "            # The label propagation logic remains the same, as it correctly filters labels.\n",
    "            child_labels = current.prototype_labels[child_mask]\n",
    "\n",
    "            child = TreeNode(child_prototypes, child_labels, [], current)\n",
    "            current.children.append(child)\n",
    "            queue.append(child)\n",
    "\n",
    "            unassigned_mask[child_mask] = False\n",
    "            child_concept_counts = np.sum(child_prototypes, axis=0)\n",
    "            remaining_concept_counts -= child_concept_counts\n",
    "            num_remaining_prototypes -= num_child_prototypes\n",
    "\n",
    "        if num_remaining_prototypes > 0:\n",
    "            final_child_prototypes = current.prototypes[unassigned_mask]\n",
    "            final_child_labels = current.prototype_labels[unassigned_mask]\n",
    "            child = TreeNode(final_child_prototypes, final_child_labels, [], current)\n",
    "            current.children.append(child)\n",
    "            queue.append(child)\n",
    "\n",
    "    return root"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "4c4f7e54",
   "metadata": {},
   "outputs": [],
   "source": [
    "def find_node_by_id(root, target_id):\n",
    "    if root.node_id == target_id:\n",
    "        return root\n",
    "    for child in root.children:\n",
    "        result = find_node_by_id(child, target_id)\n",
    "        if result:\n",
    "            return result\n",
    "    return None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82ffa0cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.patches import Rectangle\n",
    "import matplotlib.patches as mpatches\n",
    "import numpy as np\n",
    "from collections import deque\n",
    "\n",
    "# Helper classes and functions (Node, find_node_by_id) should be defined here as before.\n",
    "class Node:\n",
    "    def __init__(self, node_id, concepts=None, prototypes=None, prototype_labels=None, is_leaf=False, parent=None):\n",
    "        self.node_id = node_id\n",
    "        self.concepts = concepts if concepts is not None else []\n",
    "        self.prototypes = prototypes if prototypes is not None else np.array([])\n",
    "        self.prototype_labels = prototype_labels if prototype_labels is not None else np.array([])\n",
    "        self.is_leaf = is_leaf\n",
    "        self.parent = parent\n",
    "        self.children = []\n",
    "\n",
    "    def add_child(self, child_node):\n",
    "        self.children.append(child_node)\n",
    "        child_node.parent = self\n",
    "\n",
    "def find_node_by_id(root, node_id):\n",
    "    if not root:\n",
    "        return None\n",
    "    queue = deque([root])\n",
    "    while queue:\n",
    "        current = queue.popleft()\n",
    "        if current.node_id == node_id:\n",
    "            return current\n",
    "        for child in current.children:\n",
    "            queue.append(child)\n",
    "    return None\n",
    "\n",
    "\n",
    "def visualize_tree(\n",
    "    root,\n",
    "    class_names,\n",
    "    concept_names,\n",
    "    figsize=(16, 10),\n",
    "    max_leaf_protos_to_display=3,\n",
    "    max_internal_protos_to_display=4\n",
    "):\n",
    "    \"\"\"\n",
    "    Visualizes a concept tree with visually square nodes.\n",
    "    \"\"\"\n",
    "    fig, ax = plt.subplots(figsize=figsize)\n",
    "    ax.axis('off')\n",
    "\n",
    "    if not root:\n",
    "        print(\"Tree is empty.\")\n",
    "        plt.show()\n",
    "        return\n",
    "\n",
    "    # 1. Calculate node positions\n",
    "    positions = {}\n",
    "    def assign_positions(node, x=0, y=0, width=1):\n",
    "        positions[node.node_id] = (x + width / 2, y)\n",
    "        if node.children:\n",
    "            child_width = width / len(node.children)\n",
    "            for i, child in enumerate(node.children):\n",
    "                child_x = x + i * child_width\n",
    "                assign_positions(child, child_x, y - 1, child_width)\n",
    "    assign_positions(root, 0, 0, 1)\n",
    "\n",
    "    # Finalize axis limits *before* calculating the aspect ratio\n",
    "    ax.set_xlim(-0.1, 1.1)\n",
    "    ax.set_ylim(min(pos[1] for pos in positions.values()) - 0.5, 0.5)\n",
    "\n",
    "    # --- NEW: Calculate aspect ratio to make nodes square ---\n",
    "    # Get the ratio of y-span to x-span in data coordinates\n",
    "    x_range = ax.get_xlim()[1] - ax.get_xlim()[0]\n",
    "    y_range = ax.get_ylim()[1] - ax.get_ylim()[0]\n",
    "    # Get the ratio of height to width in display coordinates (pixels)\n",
    "    fig_width, fig_height = fig.get_size_inches()\n",
    "    # This is the crucial ratio that corrects for the figure's shape\n",
    "    aspect_ratio = (y_range / x_range) * (fig_width / fig_height)\n",
    "\n",
    "    font_size = 11\n",
    "\n",
    "    # 2. Draw nodes and edges\n",
    "    for node_id, (x, y) in positions.items():\n",
    "        node = find_node_by_id(root, node_id)\n",
    "        if not node: continue\n",
    "\n",
    "        # (The logic for label text generation is unchanged)\n",
    "        if node.is_leaf:\n",
    "            color = 'lightgreen'\n",
    "            differentiating_concepts = set()\n",
    "            parent = node.parent\n",
    "            if parent and all(c.is_leaf for c in parent.children) and len(parent.children) > 1:\n",
    "                my_concepts = set(np.where(node.prototypes[0] == 1)[0]) if node.prototypes.size > 0 else set()\n",
    "                sibling_concepts_union = set()\n",
    "                for sibling in parent.children:\n",
    "                    if sibling is not node and sibling.prototypes.size > 0:\n",
    "                        sibling_concepts_union.update(np.where(sibling.prototypes[0] == 1)[0])\n",
    "                differentiating_concepts = my_concepts - sibling_concepts_union\n",
    "\n",
    "            label_parts = []\n",
    "            if differentiating_concepts:\n",
    "                diff_list = sorted(list(differentiating_concepts))[:3]\n",
    "                # Look up concept names using the indices\n",
    "                diff_names = [concept_names[c] for c in diff_list]\n",
    "                label_parts.append(\", \".join(diff_names))\n",
    "\n",
    "            if len(node.prototype_labels) <= max_leaf_protos_to_display:\n",
    "                # Original line:\n",
    "                label_parts.append(f\"P{node.prototype_labels.tolist()[0]}: \")\n",
    "\n",
    "                # New lines:\n",
    "                # Use the indices in prototype_labels to look up names from the list\n",
    "                proto_names = [class_names[i] for i in node.prototype_labels]\n",
    "                label_parts.append(f\"{proto_names[0].capitalize()}\")\n",
    "\n",
    "            else:\n",
    "                label_parts.append(f\"({len(node.prototypes)})\")\n",
    "            label = \"\\n\".join(label_parts)\n",
    "        else:\n",
    "            color = 'lightblue'\n",
    "            if node.concepts:\n",
    "                max_concepts_in_label = 3\n",
    "                # Look up concept names using the indices in node.concepts\n",
    "                concept_name_list = [concept_names[c] for c in node.concepts]\n",
    "\n",
    "                if len(concept_name_list) > max_concepts_in_label:\n",
    "                    concepts_text = \", \".join(concept_name_list[:max_concepts_in_label])\n",
    "                    concepts_str = f\"{concepts_text}, ...\"\n",
    "                else:\n",
    "                    concepts_str = \", \".join(concept_name_list)\n",
    "            else:\n",
    "                concepts_str = \"Root\"\n",
    "\n",
    "            if len(node.prototypes) <= max_internal_protos_to_display:\n",
    "                proto_str = f\"P{node.prototype_labels.tolist()}\"\n",
    "            else:\n",
    "                proto_str = f\"({len(node.prototypes)})\"\n",
    "            label = f\"{concepts_str}\\n{proto_str}\"\n",
    "\n",
    "        # --- MODIFICATION: Set box size using the calculated aspect ratio ---\n",
    "        default_box_height = 0.4\n",
    "        default_box_width = default_box_height / aspect_ratio\n",
    "        font_size = 11\n",
    "\n",
    "        # Measure text size\n",
    "        renderer = fig.canvas.get_renderer()\n",
    "        text_obj = ax.text(0, 0, label, fontsize=font_size, weight='bold')\n",
    "        bbox = text_obj.get_window_extent(renderer=renderer)\n",
    "        text_width_disp = bbox.width / fig.dpi  # inches\n",
    "\n",
    "        # Convert display width to data coordinates\n",
    "        disp_to_data = x_range / fig_width\n",
    "        required_box_width = text_width_disp * disp_to_data + 0.02  # 0.02 for padding\n",
    "\n",
    "        box_width = max(default_box_width, required_box_width)\n",
    "        box_height = default_box_height\n",
    "\n",
    "        # Remove the temporary text object\n",
    "        text_obj.remove()\n",
    "\n",
    "        # Draw the node box and text\n",
    "        rect = Rectangle((x - box_width / 2, y - box_height / 2), box_width, box_height,\n",
    "                         facecolor=color, edgecolor='black', linewidth=1.5, zorder=3)\n",
    "        ax.add_patch(rect)\n",
    "        ax.text(x, y, label, ha='center', va='center', fontsize=font_size, weight='bold', zorder=4)\n",
    "\n",
    "        # Draw clean edges to children\n",
    "        for child in node.children:\n",
    "            child_x, child_y = positions[child.node_id]\n",
    "            ax.plot([x, child_x], [y - box_height / 2, child_y + box_height / 2], 'k-', linewidth=1, zorder=2)\n",
    "\n",
    "    # 3. Finalize plot appearance\n",
    "    ax.set_aspect('auto') # Explicitly keep auto aspect\n",
    "    # ax.set_title('Concept Tree Visualization', fontsize=20, weight='bold', pad=20)\n",
    "\n",
    "    leaf_patch = mpatches.Patch(color='lightgreen', label='Leaf Node', ec='black')\n",
    "    internal_patch = mpatches.Patch(color='lightblue', label='Internal Node', ec='black')\n",
    "    ax.legend(handles=[internal_patch, leaf_patch], loc='upper right', frameon=False, fontsize=12)\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plot_dir = os.path.join(PROJECT_ROOT, 'output', 'plots', 'explanation')\n",
    "    os.makedirs(plot_dir, exist_ok=True)\n",
    "    plt.savefig(os.path.join(plot_dir, f'{use_dataset}_concept_tree.png'), dpi=600, bbox_inches='tight')\n",
    "\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "9c2e3589",
   "metadata": {},
   "outputs": [],
   "source": [
    "import plotly.graph_objects as go\n",
    "import numpy as np\n",
    "\n",
    "def visualize_tree_plotly(\n",
    "    root,\n",
    "    figsize=(1080,720),\n",
    "    max_leaf_protos_to_display=3,\n",
    "    max_internal_protos_to_display=4 # CHANGE 1: New parameter\n",
    "):\n",
    "    if not root:\n",
    "        print(\"Tree is empty.\")\n",
    "        return\n",
    "\n",
    "    # --- 1. Layout Algorithm (Unchanged) ---\n",
    "    positions = {}\n",
    "\n",
    "    def set_depths(node, depth=0):\n",
    "        node.depth = depth\n",
    "        for child in node.children:\n",
    "            set_depths(child, depth + 1)\n",
    "\n",
    "    def assign_and_center_positions(node, leaf_counter=[0]):\n",
    "        if not node.children:\n",
    "            positions[node.node_id] = (leaf_counter[0], -node.depth)\n",
    "            leaf_counter[0] += 1\n",
    "        else:\n",
    "            for child in node.children:\n",
    "                assign_and_center_positions(child, leaf_counter)\n",
    "            first_child_x = positions[node.children[0].node_id][0]\n",
    "            last_child_x = positions[node.children[-1].node_id][0]\n",
    "            parent_x = (first_child_x + last_child_x) / 2\n",
    "            positions[node.node_id] = (parent_x, -node.depth)\n",
    "\n",
    "    set_depths(root)\n",
    "    assign_and_center_positions(root)\n",
    "\n",
    "    # --- 2. Prepare data for Plotly traces ---\n",
    "    edge_x, edge_y, node_x, node_y = [], [], [], []\n",
    "    node_colors, node_hover_text, annotations = [], [], []\n",
    "\n",
    "    queue = deque([root])\n",
    "    visited = {root.node_id}\n",
    "\n",
    "    while queue:\n",
    "        node = queue.popleft()\n",
    "        x, y = positions[node.node_id]\n",
    "        node_x.append(x)\n",
    "        node_y.append(y)\n",
    "\n",
    "        if node.is_leaf:\n",
    "            color = 'lightgreen'\n",
    "\n",
    "            # ### CHANGE 2 & 3: NEW LOGIC FOR LEAF NODES ###\n",
    "\n",
    "            # --- Part A: Calculate differentiating and inherited concepts ---\n",
    "            inherited_concepts = set()\n",
    "            current = node.parent\n",
    "            while current:\n",
    "                inherited_concepts.update(current.concepts)\n",
    "                current = current.parent\n",
    "\n",
    "            differentiating_concepts = set()\n",
    "            parent = node.parent\n",
    "            if parent and all(child.is_leaf for child in parent.children) and len(parent.children) > 1:\n",
    "                my_concepts = set(np.where(node.prototypes[0] == 1)[0])\n",
    "                sibling_concepts_union = set()\n",
    "                for sibling in parent.children:\n",
    "                    if sibling is not node:\n",
    "                        sibling_concepts_union.update(np.where(sibling.prototypes[0] == 1)[0])\n",
    "                differentiating_concepts = my_concepts - sibling_concepts_union\n",
    "\n",
    "            # --- Part B: Create the node label ---\n",
    "            label_parts = []\n",
    "\n",
    "            # Add up to 3 differentiating concepts to the label (without \"D:\")\n",
    "            if differentiating_concepts:\n",
    "                diff_list = sorted(list(differentiating_concepts))[:2]\n",
    "                label_parts.append(\", \".join(f\"C{c}\" for c in diff_list))\n",
    "\n",
    "            # Add prototype IDs or count to the label\n",
    "            if len(node.prototype_labels) <= max_leaf_protos_to_display:\n",
    "                label_parts.append(f\"P{node.prototype_labels.tolist()}\")\n",
    "            else:\n",
    "                label_parts.append(f\"({len(node.prototypes)})\")\n",
    "\n",
    "            label = \"<br>\".join(label_parts)\n",
    "\n",
    "            # --- Part C: Create the unified hover text ---\n",
    "            full_concept_set = inherited_concepts.union(differentiating_concepts)\n",
    "            full_concepts_str = \", \".join(sorted([f\"C{int(c)}\" for c in full_concept_set])) or \"None\"\n",
    "\n",
    "            hover_text = (f\"<b>Leaf Node ID:</b> {node.node_id}<br>\"\n",
    "                          f\"<b>Prototype Label(s):</b> {node.prototype_labels.tolist()}<br>\"\n",
    "                          f\"<b>Full Concepts:</b> {full_concepts_str}\")\n",
    "\n",
    "        else: # Internal node\n",
    "            color = 'lightblue'\n",
    "\n",
    "            # ### CHANGE 1: NEW LOGIC FOR INTERNAL NODES ###\n",
    "\n",
    "            # --- Part A: Create the concept line of the label ---\n",
    "            if node.concepts:\n",
    "                max_concepts_in_label = 3\n",
    "                if len(node.concepts) > max_concepts_in_label:\n",
    "                    concepts_text = \", \".join(f\"C{c}\" for c in node.concepts[:max_concepts_in_label])\n",
    "                    concepts_str = f\"{concepts_text}, ...\"\n",
    "                else:\n",
    "                    concepts_str = \", \".join(f\"C{c}\" for c in node.concepts)\n",
    "            else:\n",
    "                concepts_str = \"Root\"\n",
    "\n",
    "            # --- Part B: Create the prototype line of the label (ID list or count) ---\n",
    "            if len(node.prototypes) <= max_internal_protos_to_display:\n",
    "                proto_str = f\"P{node.prototype_labels.tolist()}\"\n",
    "            else:\n",
    "                proto_str = f\"({len(node.prototypes)})\"\n",
    "\n",
    "            label = f\"{concepts_str}<br>{proto_str}\"\n",
    "\n",
    "            # --- Part C: Hover text for internal nodes (unchanged) ---\n",
    "            clean_concepts_list = [int(c) for c in node.concepts] if node.concepts else 'None'\n",
    "            hover_text = (f\"<b>Internal Node ID:</b> {node.node_id}<br>\"\n",
    "                        f\"<b>New Concepts:</b> {clean_concepts_list}<br>\"\n",
    "                        f\"<b>Prototype Count:</b> {len(node.prototypes)}\")\n",
    "\n",
    "        node_colors.append(color)\n",
    "        node_hover_text.append(hover_text)\n",
    "        annotations.append(dict(\n",
    "                x=x,\n",
    "                y=y,\n",
    "                text=label,\n",
    "                showarrow=False,\n",
    "                font=dict(color='black', size=14),\n",
    "                xanchor='center',\n",
    "                yanchor='middle'\n",
    "            ))\n",
    "\n",
    "        for child in node.children:\n",
    "            if child.node_id not in visited:\n",
    "                child_x, child_y = positions[child.node_id]\n",
    "                edge_x.extend([x, child_x, None])\n",
    "                edge_y.extend([y, child_y, None])\n",
    "                queue.append(child)\n",
    "                visited.add(child.node_id)\n",
    "\n",
    "    # --- 3. & 4. Create Traces and Assemble Figure (Unchanged) ---\n",
    "    edge_trace = go.Scatter(x=edge_x, y=edge_y, line=dict(width=1, color='black'), hoverinfo='none', mode='lines')\n",
    "    node_trace = go.Scatter(\n",
    "        x=node_x, y=node_y,\n",
    "        mode='markers',\n",
    "        hoverinfo='text',\n",
    "        hovertext=node_hover_text,\n",
    "        marker=dict(\n",
    "            symbol='square',\n",
    "            color=node_colors,\n",
    "            size=70,\n",
    "            line=dict(width=1, color='black')\n",
    "        )\n",
    "    )\n",
    "    fig = go.Figure(data=[edge_trace, node_trace])\n",
    "    legend_annotations = [go.layout.Annotation(x=0.98, y=0.98, xref=\"paper\", yref=\"paper\", text=\"Internal Node\", showarrow=False, xanchor='right', yanchor='top'), go.layout.Annotation(x=0.98, y=0.94, xref=\"paper\", yref=\"paper\", text=\"Leaf Node\", showarrow=False, xanchor='right', yanchor='top')]\n",
    "    legend_shapes = [go.layout.Shape(type=\"rect\", xref=\"paper\", yref=\"paper\", x0=1.0, y0=0.96, x1=1.02, y1=0.98, fillcolor='lightblue', line=dict(color='black')), go.layout.Shape(type=\"rect\", xref=\"paper\", yref=\"paper\", x0=1.0, y0=0.92, x1=1.02, y1=0.94, fillcolor='lightgreen', line=dict(color='black'))]\n",
    "    fig.update_layout(title=dict(text='<b>Concept Tree Visualization (Interactive)</b>', x=0.5, font=dict(size=16)), width=figsize[0], height=figsize[1], showlegend=False, annotations=annotations + legend_annotations, shapes=legend_shapes, xaxis=dict(showgrid=False, zeroline=False, showticklabels=False, visible=False), yaxis=dict(showgrid=False, zeroline=False, showticklabels=False, visible=False), plot_bgcolor='white', margin=dict(l=20, r=40, b=20, t=40), hovermode='closest')\n",
    "\n",
    "    fig.write_html(\"tree_visualization.html\")\n",
    "\n",
    "    fig.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "7a18852b",
   "metadata": {},
   "outputs": [],
   "source": [
    "idx = 0\n",
    "tree = build_concept_tree(clusters_prototypes[idx], clusters_labels[idx][0], max_leaf_size=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "1fb4e968",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 800x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "visualize_tree(tree, class_names, figsize=(8, 8))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "468a2941",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "hoverinfo": "none",
         "line": {
          "color": "black",
          "width": 1
         },
         "mode": "lines",
         "type": "scatter",
         "x": [
          2.125,
          1.25,
          null,
          2.125,
          3,
          null,
          1.25,
          0.5,
          null,
          1.25,
          2,
          null,
          0.5,
          0,
          null,
          0.5,
          1,
          null
         ],
         "y": [
          0,
          -1,
          null,
          0,
          -1,
          null,
          -1,
          -2,
          null,
          -1,
          -2,
          null,
          -2,
          -3,
          null,
          -2,
          -3,
          null
         ]
        },
        {
         "hoverinfo": "text",
         "hovertext": [
          "<b>Internal Node ID:</b> 0<br><b>New Concepts:</b> [6, 11]<br><b>Prototype Count:</b> 4",
          "<b>Internal Node ID:</b> 1<br><b>New Concepts:</b> [0]<br><b>Prototype Count:</b> 3",
          "<b>Leaf Node ID:</b> 2<br><b>Prototype Label(s):</b> [2]<br><b>Full Concepts:</b> C11, C6",
          "<b>Internal Node ID:</b> 3<br><b>New Concepts:</b> [17]<br><b>Prototype Count:</b> 2",
          "<b>Leaf Node ID:</b> 4<br><b>Prototype Label(s):</b> [4]<br><b>Full Concepts:</b> C0, C11, C6",
          "<b>Leaf Node ID:</b> 5<br><b>Prototype Label(s):</b> [3]<br><b>Full Concepts:</b> C0, C11, C17, C4, C6",
          "<b>Leaf Node ID:</b> 6<br><b>Prototype Label(s):</b> [5]<br><b>Full Concepts:</b> C0, C11, C17, C6, C8, C9"
         ],
         "marker": {
          "color": [
           "lightblue",
           "lightblue",
           "lightgreen",
           "lightblue",
           "lightgreen",
           "lightgreen",
           "lightgreen"
          ],
          "line": {
           "color": "black",
           "width": 1
          },
          "size": 70,
          "symbol": "square"
         },
         "mode": "markers",
         "type": "scatter",
         "x": [
          2.125,
          1.25,
          3,
          0.5,
          2,
          0,
          1
         ],
         "y": [
          0,
          -1,
          -1,
          -2,
          -2,
          -3,
          -3
         ]
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "C6, C11<br>P[2, 3, 4, 5]",
          "x": 2.125,
          "xanchor": "center",
          "y": 0,
          "yanchor": "middle"
         },
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "C0<br>P[3, 4, 5]",
          "x": 1.25,
          "xanchor": "center",
          "y": -1,
          "yanchor": "middle"
         },
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "P[2]",
          "x": 3,
          "xanchor": "center",
          "y": -1,
          "yanchor": "middle"
         },
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "C17<br>P[3, 5]",
          "x": 0.5,
          "xanchor": "center",
          "y": -2,
          "yanchor": "middle"
         },
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "P[4]",
          "x": 2,
          "xanchor": "center",
          "y": -2,
          "yanchor": "middle"
         },
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "C4<br>P[3]",
          "x": 0,
          "xanchor": "center",
          "y": -3,
          "yanchor": "middle"
         },
         {
          "font": {
           "color": "black",
           "size": 14
          },
          "showarrow": false,
          "text": "C8, C9<br>P[5]",
          "x": 1,
          "xanchor": "center",
          "y": -3,
          "yanchor": "middle"
         },
         {
          "showarrow": false,
          "text": "Internal Node",
          "x": 0.98,
          "xanchor": "right",
          "xref": "paper",
          "y": 0.98,
          "yanchor": "top",
          "yref": "paper"
         },
         {
          "showarrow": false,
          "text": "Leaf Node",
          "x": 0.98,
          "xanchor": "right",
          "xref": "paper",
          "y": 0.94,
          "yanchor": "top",
          "yref": "paper"
         }
        ],
        "height": 720,
        "hovermode": "closest",
        "margin": {
         "b": 20,
         "l": 20,
         "r": 40,
         "t": 40
        },
        "plot_bgcolor": "white",
        "shapes": [
         {
          "fillcolor": "lightblue",
          "line": {
           "color": "black"
          },
          "type": "rect",
          "x0": 1,
          "x1": 1.02,
          "xref": "paper",
          "y0": 0.96,
          "y1": 0.98,
          "yref": "paper"
         },
         {
          "fillcolor": "lightgreen",
          "line": {
           "color": "black"
          },
          "type": "rect",
          "x0": 1,
          "x1": 1.02,
          "xref": "paper",
          "y0": 0.92,
          "y1": 0.94,
          "yref": "paper"
         }
        ],
        "showlegend": false,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "font": {
          "size": 16
         },
         "text": "<b>Concept Tree Visualization (Interactive)</b>",
         "x": 0.5
        },
        "width": 1080,
        "xaxis": {
         "showgrid": false,
         "showticklabels": false,
         "visible": false,
         "zeroline": false
        },
        "yaxis": {
         "showgrid": false,
         "showticklabels": false,
         "visible": false,
         "zeroline": false
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "visualize_tree_plotly(tree)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0378867",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "127c1dbe",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85ff369a",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "3.11.9",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
